﻿using System;
using System.Threading;
using System.ComponentModel;
using GeoFramework.Gps.Nmea;

namespace GeoFramework.Gps.Emulators
{
	public sealed class GarminEtrexVistaNmeaEmulator : NmeaEmulator 
	{
		private DateTime pGprmcLastSent = DateTime.Now;
		private DateTime pGprmbLastSent = DateTime.Now;
        private DateTime pGpggaLastSent = DateTime.Now;
		private DateTime pGpgsaLastSent = DateTime.Now;
		private DateTime pGpgsvLastSent = DateTime.Now;
        private DateTime pGpgllLastSent = DateTime.Now;
        private DateTime pGpbodLastSent = DateTime.Now;
        //private DateTime pPgrmeLastSent = DateTime.Now;
        //private DateTime pPgrmzLastSent = DateTime.Now;
        //private DateTime pPgrmmLastSent = DateTime.Now;
        //private DateTime pHchdgLastSent = DateTime.Now;
        //private DateTime pGprteLastSent = DateTime.Now;

        private TimeSpan pGprmcInterval = new TimeSpan(0, 0, 1);
        private TimeSpan pGprmbInterval = new TimeSpan(0, 0, 1);
        private TimeSpan pGpggaInterval = new TimeSpan(0, 0, 1);
        private TimeSpan pGpgsaInterval = new TimeSpan(0, 0, 1);
        private TimeSpan pGpgsvInterval = new TimeSpan(0, 0, 5);
        private TimeSpan pGpgllInterval = new TimeSpan(0, 0, 1);
        private TimeSpan pGpbodInterval = new TimeSpan(0, 0, 1);
        //private TimeSpan pPgrmeInterval = new TimeSpan(0, 0, 1);
        //private TimeSpan pPgrmzInterval = new TimeSpan(0, 0, 1);
        //private TimeSpan pPgrmmInterval = new TimeSpan(0, 0, 1);
        //private TimeSpan pHchdgInterval = new TimeSpan(0, 0, 1);
        //private TimeSpan pGprteInterval = new TimeSpan(0, 0, 1);

/*   Sentences on an eTrex Vista are generated like this:

    $GPRMC,220558,V,3940.8977,N,10506.6718,W,17.4,0.0,210405,10.1,E,S*01
    $GPRMB,V,,,,,,,,,,,,A,S*0E
    $GPGGA,220558,3940.8977,N,10506.6718,W,8,09,2.0,1686.4,M,-21.0,M,,*4C
    $GPGSA,A,3,01,03,09,14,15,18,19,21,22,,,,3.6,2.0,3.0*3E
    $GPGSV,3,3,09,22,70,006,50*44
    $GPGLL,3940.8977,N,10506.6718,W,220558,V,S*5D
    $GPBOD,,T,,M,,*47
    $PGRME,15.0,M,22.5,M,27.0,M*1A
    $PGRMZ,5404,f*31
    $PGRMM,WGS 84*06
    $HCHDG,120.0,,,10.1,E*1A
    $GPRTE,1,1,c,*37
*/

        public GarminEtrexVistaNmeaEmulator() 
			: base("Garmin® eTrex Vista Emulator [NMEA-0183] (http://www.geoframeworks.com)")
		{
		}

        public override Emulator Clone()
        {
            GarminEtrexVistaNmeaEmulator e = new GarminEtrexVistaNmeaEmulator();
            e.AllowRandomRoutes = this.AllowRandomRoutes;
            e.Altitude = this.Altitude;
            e.BaudRate = this.BaudRate;
            e.CurrentPosition = this.CurrentPosition;
            e.DifferentialGps = this.DifferentialGps;
            e.FixMethod = this.FixMethod;
            e.FixMode = this.FixMode;
            e.FixQuality = this.FixQuality;
            e.HorizontalDilutionOfPrecision = this.HorizontalDilutionOfPrecision;
            e.IsFixed = this.IsFixed;
            e.MagneticVariation = this.MagneticVariation;
            e.MeanDilutionOfPrecision = this.MeanDilutionOfPrecision;
            e.Route.AddRange(this.Route);
            e.RouteBoundary = this.RouteBoundary;
            e.Satellites = this.Satellites;
            e.Speed = this.Speed;
#if Framework20 && !PocketPC
            // ThreadPriority is not used on DF2.0 because of stability issues
#else
            e.ThreadPriority = this.ThreadPriority;
#endif
            e.VerticalDilutionOfPrecision = this.VerticalDilutionOfPrecision;
            return e;
        }


		/// <summary>
		/// Generates actual data to send to the client.
		/// </summary>
		/// <remarks></remarks>
        protected override void OnEmulation()
        {
            base.OnEmulation();

            // $GPRMC
            if (!pGprmcInterval.Equals(TimeSpan.Zero))
            {
                // Has enough time elapsed to send the sentence?
                if (DateTime.Now.Subtract(pGprmcLastSent).TotalMilliseconds > pGprmcInterval.TotalMilliseconds)
                {
                    // Yes
                    GprmcSentence GPRMC = new GprmcSentence(UtcDateTime, IsFixed, CurrentPosition, Speed,
                        Bearing, MagneticVariation);
                    WriteToClient(GPRMC.ToString());
                    pGprmcLastSent = DateTime.Now;
                    // Simulte a slower baud rate
                }
            }

            // Has enough time elapsed to send the sentence?
            if (DateTime.Now.Subtract(pGprmbLastSent).TotalMilliseconds > pGprmbInterval.TotalMilliseconds)
            {
                // $GPRMB
                WriteLineToClient("$GPRMB,V,,,,,,,,,,,,A,S*0E");
                pGprmbLastSent = DateTime.Now;
            }

            // $GPGGA
            if (!pGpggaInterval.Equals(TimeSpan.Zero))
            {
                // Has enough time elapsed to send the sentence?
                if (DateTime.Now.Subtract(pGpggaLastSent).TotalMilliseconds > pGpggaInterval.TotalMilliseconds)
                {
                    // Yes
                    GpggaSentence GPGGA = new GpggaSentence(UtcDateTime, CurrentPosition, FixQuality, Satellites.FixedSatellites.Count,
                        HorizontalDilutionOfPrecision, Altitude, Distance.Empty, DifferentialGps.Age,
                        DifferentialGps.StationId);
                    WriteToClient(GPGGA.ToString());
                    pGpggaLastSent = DateTime.Now;
                    // Simulte a slower baud rate
                }
            }

            // $GPGSA
            if (!pGpgsaInterval.Equals(TimeSpan.Zero))
            {
                // Has enough time elapsed to send the sentence?
                if (DateTime.Now.Subtract(pGpgsaLastSent).TotalMilliseconds > pGpgsaInterval.TotalMilliseconds)
                {
                    // Yes
                    GpgsaSentence GPGSA = new GpgsaSentence(FixMode, FixMethod, Satellites.FixedSatellites,
                        MeanDilutionOfPrecision, HorizontalDilutionOfPrecision, VerticalDilutionOfPrecision);
                    WriteToClient(GPGSA.ToString());
                    pGpgsaLastSent = DateTime.Now;
                    // Simulte a slower baud rate
                }
            }

            // $GPGSV
            if (!pGpgsvInterval.Equals(TimeSpan.Zero))
            {
                // Has enough time elapsed to send the sentence?
                if (DateTime.Now.Subtract(pGpgsvLastSent).TotalMilliseconds > pGpgsvInterval.TotalMilliseconds)
                {
                    // Yes
                    foreach (GpgsvSentence GPGSV in GpgsvSentence.FromSatellites(Satellites))
                    {
                        WriteToClient(GPGSV.ToString());
                    }
                    pGpgsvLastSent = DateTime.Now;
                }
            }

            // $GPGLL
            if (!pGpgllInterval.Equals(TimeSpan.Zero))
            {
                // Has enough time elapsed to send the sentence?
                if (DateTime.Now.Subtract(pGpgllLastSent).TotalMilliseconds > pGpgllInterval.TotalMilliseconds)
                {
                    // Yes
                    GpgllSentence GPGLL = new GpgllSentence(CurrentPosition, UtcDateTime, IsFixed);
                    WriteToClient(GPGLL.ToString());
                    pGpgllLastSent = DateTime.Now;
                }
            }

            // Has enough time elapsed to send the sentence?
            if (DateTime.Now.Subtract(pGpbodLastSent).TotalMilliseconds > pGpbodInterval.TotalMilliseconds)
            {
                // $GPRMB
                WriteLineToClient("$GPBOD,,T,,M,,*47");
                WriteLineToClient("$PGRME,15.0,M,22.5,M,27.0,M*1A");
                WriteLineToClient("$PGRMZ,5404,f*31");
                WriteLineToClient("$PGRMM,WGS 84*06");
                WriteLineToClient("$HCHDG,120.0,,,10.1,E*1A");
                WriteLineToClient("$GPRTE,1,1,c,*37"); 
                pGpbodLastSent = DateTime.Now;
            }

            // Sleep for a bit
            Thread.Sleep(250);
        }

        public override string ToString()
        {
            return "Garmin® eTrex Vista Emulator (NMEA-0183)";
        }
	}
}
